package pl.edu.agh.student.janikhankus.earley;

import java.util.ArrayList;
import java.util.List;

import pl.edu.agh.student.janikhankus.tree.NonTerminal;
import pl.edu.agh.student.janikhankus.tree.Production;
import pl.edu.agh.student.janikhankus.tree.Symbol;

/**
 * Class representing production in Earley's production set. Extneds normal productions by adding h,i and dot fields.
 * @author tom
 *
 */
public class ParseProduction extends Production {
	private int h;
	private int dot;
	private int i;
	private Production p;
	protected boolean isFinal;
	private ArrayList<Symbol> right;

	
	/**
	 * Creates new Parse Productions by saving reference to passed Production
	 * @param h H index. Determine first terminal which to parse tree od this production
	 * @param dot Dot index. Determine progress of production analysis.
	 * @param i I index. Define first not examined terminal.
	 * @param p Production reference
	 */
	public ParseProduction(int h, int dot, int i, Production p) {
		this.h = h;
		this.dot = dot;
		this.i = i;
		this.p = p;
		if (p == null) {
			isFinal = false;
		} else
			this.right = p.getRight();
	}

	/**
	 * Creates new Parse Productions by creating new Production
	 * @param h H index. Determine first terminal which to parse tree od this production
	 * @param dot Dot index. Determine progress of production analysis.
	 * @param i I index. Define first not examined terminal.
	 * @param p Production reference
	 * @param left Left side of Production (non terminal)
	 * @param right Right side of Production
	 * @param probability Production's probability
	 */
	public ParseProduction(int h, int dot, int i, NonTerminal left,
			ArrayList<Symbol> right, double probability) {
		this.h = h;
		this.dot = dot;
		this.i = i;
		this.p = new Production();
		isFinal = false;
		p.setLeft(left);
		p.setRight(right);
		p.setProbability(probability);
		this.right = right;
	}

	/**
	 * @return H index
	 */
	public int getH() {
		return h;
	}

	/**
	 * @return Dot index
	 */
	public int getDot() {
		return dot;
	}

	/**
	 * @return I index
	 */
	public int getI() {
		return i;
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.student.janikhankus.tree.Production#getLeft()
	 */
	@Override
	public NonTerminal getLeft() {
		return p.getLeft();
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.student.janikhankus.tree.Production#getRight()
	 */
	@Override
	public ArrayList<Symbol> getRight() {
		// return p.getRight();
		return right;
	}

	/* (non-Javadoc)
	 * @see pl.edu.agh.student.janikhankus.tree.Production#getProbability()
	 */
	@Override
	public double getProbability() {
		return p.getProbability();
	}

	public boolean isFinalProduction() {
		return isFinal;
	}

	@Override
	public boolean equals(Object o) {
		if (!(o instanceof ParseProduction)) {
			return false;
		}
		ParseProduction p = (ParseProduction) o;

		List<Symbol> tmpSymbolList = p.getRight();
		List<Symbol> tmpThisSymbolList = this.getRight();

		if (this == p) {
			return true;
		}
		if (tmpSymbolList.size() != tmpThisSymbolList.size()) {
			return false;
		}
		for (int i = 0; i < tmpThisSymbolList.size(); i++) {
			if (!tmpSymbolList.get(i).getName()
					.equals(tmpThisSymbolList.get(i).getName())) {
				return false;
			}
		}
		NonTerminal thisLeft = this.getLeft();
		NonTerminal pLeft = p.getLeft();
		if (thisLeft == null || pLeft == null) {
			if (thisLeft != pLeft) {
				return false;
			}
		} else {
			if (!thisLeft.getName().equals(pLeft.getName())) {
				return false;
			}
		}
		if (this.getDot() == p.getDot() && this.getH() == p.getH()
				&& this.getI() == p.getI()) {
			return true;
		}
		return false;
	}

	@Override
	public int compareTo(Object o) {
		if (!(o instanceof ParseProduction)) {
			return 1;
		}
		ParseProduction p = (ParseProduction) o;

		List<Symbol> tmpSymbolList = p.getRight();
		List<Symbol> tmpThisSymbolList = this.getRight();
		if (this == p) {
			return 0;
		}
		if (tmpSymbolList.size() != tmpThisSymbolList.size()) {
			return 1;
		}
		for (int i = 0; i < tmpThisSymbolList.size(); i++) {
			if (!tmpSymbolList.get(i).getName()
					.equals(tmpThisSymbolList.get(i).getName())) {
				return 1;
			}
		}
		NonTerminal thisLeft = this.getLeft();
		NonTerminal pLeft = p.getLeft();
		if (thisLeft == null || pLeft == null) {
			if (thisLeft != pLeft) {
				return 1;
			}
		} else {
			if (!thisLeft.getName().equals(pLeft.getName())) {
				return 1;
			}
		}
		if (this.getDot() == p.getDot() && this.getH() == p.getH()
				&& this.getI() == p.getI()) {
			return 0;
		}
		return 1;
	}

	@Override
	public int hashCode() {
		if (getLeft() != null)
			return (new Integer(getDot() * getH() + 7936
					* (getI() + getLeft().getName().hashCode()))).hashCode();
		else
			return (new Integer(getDot() * getH() + 7936 * (getI())))
					.hashCode();
	}

}
